#region Using References

using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using Beaker.OpenCube.Game.Converters;
using Beaker.OpenCube.Mapping;

#endregion

namespace Beaker.OpenCube.Game
{
	public abstract class MobData
		: EntityData
	{
		public MobData()
		{
			Contract.Ensures( _activeEffects != null );

			_activeEffects = new List<PotionEffect>();
		}

		[ContractInvariantMethod]
		private void ContractInvariant()
		{
			Contract.Invariant( _health >= 0 );
			Contract.Invariant( _attackTime >= TimeSpan.Zero );
			Contract.Invariant( _hurtTime >= TimeSpan.Zero );
			Contract.Invariant( _deathTime >= TimeSpan.Zero );
			Contract.Invariant( _activeEffects != null );
		}

		private short _health;

		/// <summary>
		/// Gets or sets the health the entity has. A value of 1 is half a heart.
		/// </summary>
		/// <value>
		/// The health the entity has.
		/// </value>
		public short Health
		{
			get
			{
				Contract.Ensures( Contract.Result<short>() >= 0 );
				Contract.Ensures( Contract.Result<short>() == _health );

				return _health;
			}
			set
			{
				Contract.Requires<ArgumentOutOfRangeException>( value >= 0, "Health cannot be negative." );
				Contract.Ensures( _health == value );

				SetValue( ref _health, value );
			}
		}

		private TimeSpan _attackTime;

		/// <summary>
		/// Gets or sets the time the mob's "invincibility shield" lasts after the mob was last struck.
		/// </summary>
		/// <value>
		/// The time the mob's "invincibility shield" lasts after the mob was last struck.
		/// </value>
		[Mapping( Converter = typeof( GameTicksTimeSpanConverter ) )]
		public TimeSpan AttackTime
		{
			get
			{
				Contract.Ensures( Contract.Result<TimeSpan>() >= TimeSpan.Zero );
				Contract.Ensures( Contract.Result<TimeSpan>() == _attackTime );

				return _attackTime;
			}
			set
			{
				Contract.Requires<ArgumentOutOfRangeException>( value >= TimeSpan.Zero, "AttackTime cannot be negative" );
				Contract.Ensures( _attackTime == value );

				SetValue( ref _attackTime, value );
			}
		}

		private TimeSpan _hurtTime;

		/// <summary>
		/// Gets or sets the hurt time this mob turns red after being hit.
		/// </summary>
		/// <value>
		/// The hurt time this mob turns red after being hit.
		/// </value>
		[Mapping( Converter = typeof( GameTicksTimeSpanConverter ) )]
		public TimeSpan HurtTime
		{
			get
			{
				Contract.Ensures( Contract.Result<TimeSpan>() >= TimeSpan.Zero );
				Contract.Ensures( Contract.Result<TimeSpan>() == _hurtTime );

				return _hurtTime;
			}
			set
			{
				Contract.Requires<ArgumentOutOfRangeException>( value >= TimeSpan.Zero, "HurtTime cannot be negative" );
				Contract.Ensures( _hurtTime == value );

				SetValue( ref _hurtTime, value );
			}
		}

		private TimeSpan _deathTime;

		/// <summary>
		/// Gets or sets the time this mob has been dead. Controls death animations.
		/// </summary>
		/// <value>
		/// The time this mob has been dead.
		/// </value>
		[Mapping( Converter = typeof( GameTicksTimeSpanConverter ) )]
		public TimeSpan DeathTime
		{
			get
			{
				Contract.Ensures( Contract.Result<TimeSpan>() >= TimeSpan.Zero );
				Contract.Ensures( Contract.Result<TimeSpan>() == _deathTime );

				return _deathTime;
			}
			set
			{
				Contract.Requires<ArgumentOutOfRangeException>( value >= TimeSpan.Zero, "DeathTime cannot be negative" );
				Contract.Ensures( _deathTime == value );

				SetValue( ref _deathTime, value );
			}
		}

		private readonly List<PotionEffect> _activeEffects;

		/// <summary>
		/// Gets the list of active effects.
		/// </summary>
		/// <value>
		/// The list of active effects.
		/// </value>
		[Mapping( IsOptional = true )]
		public List<PotionEffect> ActiveEffects
		{
			get
			{
				Contract.Ensures( Contract.Result<List<PotionEffect>>() != null );
				return _activeEffects;
			}
		}

		private readonly List<Item> _equipment = new List<Item>( new Item[] { null, null, null, null, null } );

		[Mapping( ListFixedSize = 5, IsOptional = true )]
		public List<Item> Equipment
		{
			get
			{
				Contract.Ensures( Contract.Result<List<Item>>() != null );
				return _equipment;
			}
		}
	}
}